1.2.1 umi 使用

umi,中文可发音为乌米,是一个可插拔的企业级 react 应用框架。umi 以路由为基础的,支持类 next.js 的约定式路由,以及各种进阶的路由功能,并以此进行功能扩展,比如支持路由级的按需加载。然后配以完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求,目前内外部加起来已有 50+ 的插件。

📦 开箱即用,内置 react、react-router 等 🏈 类 next.js 且功能完备的路由约定,同时支持配置的路由方式 🎉 完善的插件体系,覆盖从源码到构建产物的每个生命周期 🚀 高性能,通过插件支持 PWA、以路由为单元的 code splitting 等 💈 支持静态页面导出,适配各种环境,比如中台业务、无线业务、egg、支付宝钱包、云凤蝶等 🚄 开发启动快,支持一键开启 dll 和 hard-source-webpack-plugin 等 🐠 一键兼容到 IE9,基于 umi-plugin-polyfills 🍁 完善的 TypeScript 支持,包括 d.ts 定义和 umi test 🌴 与 dva 数据流的深入融合,支持 duck directory、model 的自动加载、code splitting 等等

install

# 全局安装 umi

yarn global add umi

使用 yarn 安装后全局还是不能使用 umi,
[解决方法](https://github.com/fairyly/mynodejs/blob/gh-pages/1.2.5%20%E8%A7%A3%E5%86%B3yarn%E5%85%A8%E5%B1%80%E5%AE%89%E8%A3%85%E6%A8%A1%E5%9D%97%E5%90%8E%E4%BD%86%E4%BB%8D%E6%8F%90%E7%A4%BA%E6%97%A0%E6%B3%95%E6%89%BE%E5%88%B0%E5%91%BD%E4%BB%A4%E7%9A%84%E9%97%AE%E9%A2%98.md)

最后改用 npm i -g umi

出现问题:
npm ERR! code ETARGET
npm ERR! notarget No matching version found for @babel/generator@^7.2.2
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.
npm ERR! notarget
npm ERR! notarget It was specified as a dependency of '@babel/core'
npm ERR! notarget

由于原来使用的是公司的源,就切换到 淘宝源之后重新安装就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

usage(test)

建个空目录:
mkdir myapp && cd myapp

通过 umi g 创建一些页面:

$ umi g page index
$ umi g page users

1
2
3
4
5
6
7
8

通过脚手架创建项目

mkdir myapp && cd myapp
npm create umi

选择 app

? Do you want to use typescript? (y/N) n

选择 antd



创建完成 

npm start 启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14

目录

一个复杂应用的目录结构如下:

.
├── dist/                          // 默认的 build 输出目录
├── mock/                          // mock 文件所在目录,基于 express
├── config/
    ├── config.js                  // umi 配置,同 .umirc.js,二选一
└── src/                           // 源码目录,可选
    ├── layouts/index.js           // 全局布局
    ├── pages/                     // 页面目录,里面的文件即路由
        ├── .umi/                  // dev 临时目录,需添加到 .gitignore
        ├── .umi-production/       // build 临时目录,会自动删除
        ├── document.ejs           // HTML 模板
        ├── 404.js                 // 404 页面
        ├── page1.js               // 页面 1,任意命名,导出 react 组件
        ├── page1.test.js          // 用例文件,umi test 会匹配所有 .test.js 和 .e2e.js 结尾的文件
        └── page2.js               // 页面 2,任意命名
    ├── global.css                 // 约定的全局样式文件,自动引入,也可以用 global.less
    ├── global.js                  // 可以在这里加入 polyfill
    ├── app.js                     // 运行时配置文件
├── .umirc.js                      // umi 配置,同 config/config.js,二选一
├── .env                           // 环境变量
└── package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

.umi 中会生成一些配置文件,如本地路由配置等
注意:(不要直接在这里修改代码,umi 重启或者 pages 下的文件修改都会重新生成这个文件夹下的文件。)

路由

介绍的路由使用可以在 umi-examples/routesumi-examples/routes-via-config 里找到示例代码

约定式路由

约定 pages 下所有的 js、jsx、ts 和 tsx 文件即路由。

基础路由

假设 pages 目录结构如下:

+ pages/  
  + users/  
    - index.js  
    - list.js  
  - index.js  
1
2
3
4
5

那么,umi 会自动生成路由配置如下:

[
  { path: '/', component: './pages/index.js' },
  { path: '/users/', component: './pages/users/index.js' },
  { path: '/users/list', component: './pages/users/list.js' },
]
1
2
3
4
5

动态路由

umi 里约定,带 $ 前缀的目录或文件为动态路由。

比如以下目录结构:

+ pages/
  + $post/
    - index.js
    - comments.js
  + users/
    $id.js
  - index.js
1
2
3
4
5
6
7

会生成路由配置如下:

[
  { path: '/', component: './pages/index.js' },
  { path: '/users/:id', component: './pages/users/$id.js' },
  { path: '/:post/', component: './pages/$post/index.js' },
  { path: '/:post/comments', component: './pages/$post/comments.js' },
]
1
2
3
4
5
6

可选的动态路由

umi 里约定动态路由如果带 $ 后缀,则为可选动态路由。

比如以下结构:

+ pages/
  + users/
    - $id$.js
  - index.js
1
2
3
4

会生成路由配置如下:

[
  { path: '/': component: './pages/index.js' },
  { path: '/users/:id?': component: './pages/users/$id$.js' },
]
1
2
3
4
嵌套路由

umi 里约定目录下有 _layout.js 时会生成嵌套路由,以 _layout.js 为该目录的 layout 。

比如以下目录结构:

+ pages/
  + users/
    - _layout.js
    - $id.js
    - index.js
1
2
3
4
5

会生成路由配置如下:

[
  { path: '/users', component: './pages/users/_layout.js',
    routes: [
     { path: '/users/', component: './pages/users/index.js' },
     { path: '/users/:id', component: './pages/users/$id.js' },
   ],
  },
]
1
2
3
4
5
6
7
8

全局 layout

约定 src/layouts/index.js 为全局路由,返回一个 React 组件,通过 props.children 渲染子组件。

比如:

export default function(props) {
  return (
    <>
      <Header />
      { props.children }
      <Footer />
    </>
  );
}
1
2
3
4
5
6
7
8
9
不同的全局 layout

你可能需要针对不同路由输出不同的全局 layout,umi 不支持这样的配置,
但你仍可以在 layouts/index.js 对 location.path 做区分,渲染不同的 layout 。

比如想要针对 /login 输出简单布局,

export default function(props) {
  if (props.location.pathname === '/login') {
    return <SimpleLayout>{ props.children }</SimpleLayout>
  }

  return (
    <>
      <Header />
      { props.children }
      <Footer />
    </>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
404 路由

约定 pages/404.js 为 404 页面,需返回 React 组件。

比如:

export default () => {
  return (
    <div>I am a customized 404 page</div>
  );
};
1
2
3
4
5

注意:开发模式下,umi 会添加一个默认的 404 页面来辅助开发,但你仍然可通过精确地访问 /404 来验证 404 页面。

配置式路由

如果你倾向于使用配置式的路由,可以配置 routes ,此配置项存在时则不会对 src/pages 目录做约定式的解析。

比如:

export default {
  routes: [
    { path: '/', component: './a' },
    { path: '/list', component: './b', Routes: ['./routes/PrivateRoute.js'] },
    { path: '/users', component: './users/_layout',
      routes: [
        { path: '/users/detail', component: './users/detail' },
        { path: '/users/:id', component: './users/id' }
      ]
    },
  ],
};
1
2
3
4
5
6
7
8
9
10
11
12

注意:component 是相对于 src/pages 目录的

权限路由

umi 的权限路由是通过配置路由的 Routes 属性来实现。约定式的通过 yaml 注释添加,配置式的直接配上即可。

比如有以下配置:

[
  { path: '/', component: './pages/index.js' },
  { path: '/list', component: './pages/list.js', Routes: ['./routes/PrivateRoute.js'] },
]
1
2
3
4

然后 umi 会用 ./routes/PrivateRoute.js 来渲染 /list。

./routes/PrivateRoute.js 文件示例:

export default (props) => {
  return (
    <div>
      <div>PrivateRoute (routes/PrivateRoute.js)</div>
      { props.children }
    </div>
  );
}
1
2
3
4
5
6
7
8
启用 Hash 路由

umi 默认是用的 Browser History,如果要用 Hash History,需配置:

export default {
  history: 'hash',
}
1
2
3

参考